import CryptoJS from 'crypto-js'
import { JSEncrypt } from 'jsencrypt'

/**
 * 数据加解密
 */
const SecurityUtil = {
  // ras
  rsaPublickey: 'MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCGT6al5kuBeMhRK5Fy5CWelrVuf64uLKpdK13IburQbNEZ8YxtedCU1t61eqWY2cLasgy2+TDoqjwbinXSH0w/obwSUUg4RmDMwh0f8mIMaqmjL0IcB8r5wuH3/lHx1XLoh685C8d4/WjkBjTyfjgiMi1vnIPm0Zt2nfG2YAwdJQIDAQAB',
  // 偏移量
  iv: 'SHYTBASESHYTBASE',
  // aes key
  aesKey: ''
}

/**
 * RSA加密
 * @param {Object} data 待加密数据
 */
const rsaEncrypt = (data) => {
  // 创建加密实例
  let jsencrypt = new JSEncrypt()
  // 初始化公钥
  jsencrypt.setPublicKey(SecurityUtil.rsaPublickey)
  // 加密数据
  return jsencrypt.encrypt(data)
}

/**
 * MD5加密
 * @param {Object} data 待加密数据
 */
const md5Encrypt = (data) => {
  return CryptoJS.MD5(data).toString().toUpperCase()
}

/**
 * AES加密
 * @param {Object} data 待加密数据
 * @param {Object} keyStr 加密密钥
 * @param {Object} iv 偏移量
 */
const aesEncrypt = (data, keyStr, iv) => {
  // 获取密钥
  keyStr = CryptoJS.enc.Utf8.parse(keyStr || SecurityUtil.aesKey)
  // 偏移量设置
  iv = CryptoJS.enc.Utf8.parse(iv || SecurityUtil.iv)
  // 元数据
  data = CryptoJS.enc.Utf8.parse(data)
  // 数据加密
  let encrypted = CryptoJS.AES.encrypt(data, keyStr, { iv: iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 })
  return encrypted.ciphertext.toString(CryptoJS.enc.Base64)
}

/**
 * AES解密
 * @param {} data 待解密数据
 * @param {} keyStr 解密密钥
 * @param {} iv 偏移量
 */
const aesDecrypt = (data, keyStr, iv) => {
  // 获取密钥
  keyStr = CryptoJS.enc.Utf8.parse(keyStr || SecurityUtil.aesKey)
  // 偏移量设置
  iv = CryptoJS.enc.Utf8.parse(iv || SecurityUtil.iv)
  let descrypted = CryptoJS.AES.decrypt(data, keyStr, { iv: iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 })
  return descrypted.toString(CryptoJS.enc.Utf8)
}

/**
 * 生成加密key
 * @param {Object} len key长度
 */
const getKey = (len) => {
  let chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split('')
  let uuid = []
  let rnd = Math.random
  for (let i = 0; i < len; i++) {
    uuid[i] = chars[0 | rnd() * chars.length]
  }
  return uuid.join('')
}

/**
 * 上送报文加密
 * @param {Object} data 待加密数据
 * @param {Object} aesKey 加密密钥
 */
const reqEncrypt = (data, aesKey) => {
  // 生成数据摘要
  let md5Digest = md5Encrypt(aesKey + data)
  // 生成数字信封
  let rsaText = rsaEncrypt(aesKey)
  // 生成加密数据
  let aesText = aesEncrypt(data, aesKey)
  let result = [md5Digest, aesText, rsaText]
  return result.join(String.fromCharCode(29)).toString()
}

/**
 * 返回报文解密
 * @param {Object} data 待解密数据
 * @param {Object} aesKey 解密密钥
 */
const respDecrypt = (data, aesKey) => {
  // 取得数据体
  let encryptData = data.substring(14)
  // 拆分控制码
  // 混淆起始点
  let confuseStartPos = parseInt(data.substring(3, 5), 16)
  // 混淆长度
  let confuseLen = parseInt(data.substring(5, 7), 16)
  // 混淆规则
  let confuseRule = parseInt(data.substring(7, 8), 16)
  // 数据原长度
  let originalLen = parseInt(data.substring(8, 14), 16)

  // 反混淆
  let confuseData = ''
  // 混淆内容
  let confuseStr = encryptData.substring(confuseStartPos, confuseStartPos + confuseLen)
  // 混淆内容长度
  let confuseStrLen = confuseStr.length
  // 追加未混淆部分
  confuseData += encryptData.substring(0, confuseStartPos)

  switch (confuseRule) {
    case 1: // 首尾对换
      confuseData += confuseStr.charAt(confuseStrLen - 1)
      confuseData += confuseStr.substring(1, confuseStrLen - 1)
      confuseData += confuseStr.charAt(0)
      break
    case 2: // 基偶对换
      for (let j = 2; j <= confuseStrLen; j++) {
        if (j % 2 === 0) {
          confuseData += confuseStr.charAt(j - 1)
          confuseData += confuseStr.charAt(j - 2)
        }
      }
      if (confuseStrLen % 2 !== 0 && confuseStrLen > 0) {
        confuseData += confuseStr.charAt(confuseStrLen - 1)
      }
      break
    default:
      break
  }

  if (confuseRule !== 0) {
    confuseData += encryptData.substring(confuseStartPos + confuseLen)
    encryptData = confuseData
  }
  // 反填充
  encryptData = encryptData.substring(0, originalLen)
  // 先解16进制
  encryptData = CryptoJS.enc.Hex.parse(encryptData)
  let cipherParams = CryptoJS.lib.CipherParams.create({
    ciphertext: encryptData
  })
  return aesDecrypt(cipherParams, aesKey)
}

export default {
  namespaced: true,
  reqEncrypt,
  md5Encrypt,
  getKey,
  respDecrypt
}
